
#include <material/glsltexteditor.h>
#include <material/materialmanager.h>
#include <material/shadereditor/shadereditordialog.h>
#include <config.h>
#include <assert.h>

#include <qabstractitemmodel.h>
#include <qevent.h>
#include <qmessagebox.h>


cceMaterialShaderBindingTreeModel::cceMaterialShaderBindingTreeModel (QObject* parent, cceMaterialShader *shader)
  : QAbstractItemModel (parent)
  , _shader (shader)
{
  connect (_shader, SIGNAL(BindingsChanged()), this, SLOT (BindingsChanged ()));

  _backgroundColors[cceMaterialShader::BindingType_Attribute * 2 + 0] = QColor (128, 128, 255);
  _backgroundColors[cceMaterialShader::BindingType_Attribute * 2 + 1] = QColor (196, 196, 255);
  _backgroundColors[cceMaterialShader::BindingType_In * 2 + 0] = QColor (128, 128, 255);
  _backgroundColors[cceMaterialShader::BindingType_In * 2 + 1] = QColor (196, 196, 255);
  _backgroundColors[cceMaterialShader::BindingType_InOut * 2 + 0] = QColor (128, 128, 255);
  _backgroundColors[cceMaterialShader::BindingType_InOut * 2 + 1] = QColor (196, 196, 255);
  _backgroundColors[cceMaterialShader::BindingType_Uniform * 2 + 0] = QColor (128, 255, 128);
  _backgroundColors[cceMaterialShader::BindingType_Uniform * 2 + 1] = QColor (196, 255, 196);
}

int cceMaterialShaderBindingTreeModel::columnCount(const QModelIndex &parent) const
{
  if (parent.isValid())
    {
      return 0;
    }
  return 3;
}

QVariant cceMaterialShaderBindingTreeModel::data (const QModelIndex &index, int role) const
{
  if (!index.isValid()) return QVariant ();
  switch (role)
    {
    case Qt::DisplayRole:
      switch (index.column())
        {
        case Column_Type:
          return cceMaterialShader::GetBindingName(_shader->GetBindingType(index.row()));
        case Column_Variable:
          return _shader->GetBindingVariable(index.row());
        case Column_Semantic:
          return _shader->GetBindingSemantic(index.row());
        default:
          break;
        }
    case Qt::BackgroundRole:
      return _backgroundColors[_shader->GetBindingType(index.row()) * 2 + index.row() % 2];
    }

  return QVariant ();
}

QModelIndex cceMaterialShaderBindingTreeModel::index (int row, int column, const QModelIndex &parent) const
{
  if (parent.isValid()) return QModelIndex ();

  return createIndex(row, column, 0);
}

QModelIndex cceMaterialShaderBindingTreeModel::parent (const QModelIndex &child) const
{
  return QModelIndex ();
}

int cceMaterialShaderBindingTreeModel::rowCount(const QModelIndex &parent) const
{
  if (parent.isValid())
    {
      return 0;
    }
  return _shader->GetNumberOfBindings();
}

QVariant cceMaterialShaderBindingTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  if (role != Qt::DisplayRole) return QVariant ();

  switch (section)
    {
    case Column_Semantic:
      return "Semantic";
    case Column_Type:
      return "Type";
    case Column_Variable:
      return "Variable";
    }
	return QVariant ();
}

void cceMaterialShaderBindingTreeModel::BindingsChanged ()
{
  reset ();
}


cceMaterialShaderEditorDialog::cceMaterialShaderEditorDialog(QWidget *parent)
  : cceMaterialEditorAbstract (parent)
  , _shader (0)
{
  _gui.setupUi(this);

  _textEditor = new GLSLTextEditor (_gui.frameContent);
  QGridLayout *layout = new QGridLayout (_gui.frameContent);
  layout->setContentsMargins(0, 0, 0, 0);\
  layout->addWidget(_textEditor);

  _proxyShader = new cceMaterialShader ();
  _bindingModel = new cceMaterialShaderBindingTreeModel (this, _proxyShader);
  _gui.treeView->setModel(_bindingModel);

  _gui.splitter->setStretchFactor(0, 1);
  _gui.splitter->setStretchFactor(1, 0);

  _gui.comboBoxType->clear();
  for (unsigned i=0; i<cceMaterialShader::Type_COUNT; ++i)
    {
      _gui.comboBoxType->addItem(cceMaterialShader::GetTypeName((cceMaterialShader::Type)i));
    }

  connect (_textEditor, SIGNAL (textChanged ()), this, SLOT (CodeChanged ()));

  _updateBindingTime.setInterval(500);
  _updateBindingTime.setSingleShot(true);
  connect (&_updateBindingTime, SIGNAL(timeout()), this, SLOT(UpdateBindings()));

}

cceMaterialShaderEditorDialog::~cceMaterialShaderEditorDialog()
{
  delete _proxyShader;
}


void cceMaterialShaderEditorDialog::SetShader(cceMaterialShader *shader)
{
  _shader = shader;
	connect (_shader, SIGNAL(Removed()), this, SLOT(ForceClose()));
  _proxyShader->SetCode(_shader->GetCode());
  _proxyShader->UpdateBindings();

  UpdateGUIFromShader();
}

void cceMaterialShaderEditorDialog::ForceClose()
{
  emit CloseEditor(_shader);
}

void cceMaterialShaderEditorDialog::on_comboBoxType_currentIndexChanged(int index)
{
  SetChanged(ShaderChanged());
}

void cceMaterialShaderEditorDialog::on_lineEditName_textChanged(const QString &text)
{
  SetChanged(ShaderChanged());
}

void cceMaterialShaderEditorDialog::CodeChanged()
{
  _updateBindingTime.start();
  SetChanged(ShaderChanged());
}

void cceMaterialShaderEditorDialog::UpdateBindings()
{
  _proxyShader->SetCode(_textEditor->toPlainText());
  _proxyShader->UpdateBindings();
}


void cceMaterialShaderEditorDialog::UpdateGUIFromShader()
{
  assert (_shader);
  _gui.lineEditName->setText (GetEditorName(_shader->GetName ()));
  _gui.comboBoxType->setCurrentIndex(_shader->GetType());
  _textEditor->setPlainText(_shader->GetCode());
}

QString cceMaterialShaderEditorDialog::GetExtension() const
{
	switch (_gui.comboBoxType->currentIndex())
		{
		case cceMaterialShader::Type_Vertex:
			return ".vert";

		case cceMaterialShader::Type_Fragment:
			return ".frag";

		case cceMaterialShader::Type_Geometry:
			return ".geom";

		case cceMaterialShader::Type_TesselationControl:
			return ".tessctrl";

		case cceMaterialShader::Type_TesselationEval:
			return ".tesseval";

		}
	return ".shd";
}

bool cceMaterialShaderEditorDialog::UpdateShaderFromGUI ()
{
  assert (_shader);

	cceMaterialShader *shd = _manager->GetShader(_gui.lineEditName->text() + GetExtension());
	if (shd && shd != _shader)
		{
			QMessageBox::information(this,
															 "Shader - " CCE_TOOL_NAME,
															 "The shader name is already in use.\nPlease enter a unique shader name.");
			return false;
		}

	_shader->SetName (_gui.lineEditName->text () + GetExtension ());
  _shader->SetType((cceMaterialShader::Type)_gui.comboBoxType->currentIndex());

  _shader->SetCode(_textEditor->toPlainText());
  _shader->UpdateBindings();
  _shader->Change();

  if (_shader->IsNew() && _manager)
    {
      _manager->AddShader(_shader);
    }
	SetChanged (false);
  return true;
}


QString cceMaterialShaderEditorDialog::GetName () const
{
  if (_shader)
    {
			return _shader->GetName();
    }
  return "";
}

bool cceMaterialShaderEditorDialog::Save()
{
  return UpdateShaderFromGUI();
}

void cceMaterialShaderEditorDialog::Close ()
{
  if (ShaderChanged ())
    {
      int result = QMessageBox::question (this,
        "Shader changed - " CCE_TOOL_NAME,
				"The shader has unsaved changes. Save changes bevor closing?",
				QMessageBox::Save, QMessageBox::Close, QMessageBox::Cancel);
      switch (result)
        {
        case QMessageBox::Save:
          Save ();
          break;

        case QMessageBox::Cancel:
          return;

        case QMessageBox::Close:
          break;
        }

    }
  emit CloseEditor(_shader);
}

bool cceMaterialShaderEditorDialog::ShaderChanged ()
{
  if (!_shader)
    {
      return false;
    }
	return _gui.lineEditName->text() != GetEditorName(_shader->GetName())
      || _gui.comboBoxType->currentIndex() != (int)_shader->GetType()
      || _textEditor->toPlainText() != _shader->GetCode();
}

